package com.sunmnet.auto.spring.mvc;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.nio.charset.Charset;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.XPath;
import org.dom4j.io.SAXReader;

import com.sunmnet.auto.spring.mvc.baen.FieldSturcture;
import com.sunmnet.auto.spring.mvc.baen.TBStructure;
import com.sunmnet.auto.spring.mvc.temp.FileType;
import com.sunmnet.auto.spring.mvc.temp.Variables;
import com.sunmnet.auto.spring.mvc.variable.IVarDeal;
import com.sunmnet.pub.db.template.Template;
import com.sunmnet.util.io.FileUtils;
import com.sunmnet.util.lang.ObjectUtils;
import com.sunmnet.util.regex.RegexUtils;

public class AutoSpringMVC {

	/** LOG */
	private final static org.slf4j.Logger LOG = org.slf4j.LoggerFactory
			.getLogger(AutoSpringMVC.class);

	/**
	 *
	 *
	 * @param conn
	 * @param tableList
	 * @throws SQLException
	 * @throws IOException
	 */
	public static void getBeanFromDB(Connection conn, List<String> tableList)
			throws SQLException, IOException {
		String[] tableArray = strList2Array(tableList);
		String[] tablePreArray = null;
		getBeanFromDB(conn, tableArray, tablePreArray);
	}

	/**
	 * 把字符串列表转换为数组
	 *
	 * @param strList
	 *            字符串列表
	 * @return 若入参为null则返回null
	 */
	private static String[] strList2Array(List<String> strList) {
		String[] strArray = null;
		if (strList != null) {
			Object[] obj = strList.toArray();
			strArray = Arrays.copyOf(obj, obj.length, String[].class);
		}
		return strArray;
	}

	/**
	 * 从数据库中的表信息创建javabean,dao,service,controller
	 *
	 * @param conn
	 *            数据库连接
	 * @param tableArray
	 *            选择要导出的表，为空则导出所有表
	 * @param tablePreArray
	 *            希望移除的（多个）表前缀
	 * @throws SQLException
	 * @throws IOException
	 */
	public static void getBeanFromDB(Connection conn, String[] tableArray,
			String[] tablePreArray) throws SQLException, IOException {

		List<TBStructure> tbs = getTBStructuresFromDB(conn, tableArray,
				tablePreArray);

		createSprintMVCs(tbs, tableArray, tablePreArray);

	}

	/**
	 * 创建模版文件
	 * @param tb    表结构
	 * @param tablePreArray 表前缀数组
	 */
	public static void createSprintMVC(TBStructure tb, String[] tablePreArray) {

		String fileName = null;
		String filePath = null;
		FileType[] ftypes = FileType.values();
		IVarDeal create = null;
		String value = null;
		String content = null;
		String parentPath = null;
		parentPath = tb.getEntityName().toLowerCase();

		for (FileType type : ftypes) {
			
			LOG.debug("处理表 {} 的  {} 类型文件", tb.getTbName(), type);

			// 取类模板
			Template beanClazz = new Template();
			beanClazz.readFile(type.getTmpFilePath());
			/* 循环处理变量 */
			Variables[] vars = Variables.values();
			for (Variables var : vars) {
				try {
					create = (IVarDeal) ObjectUtils.instanceClass(var
							.getClassName());
					LOG.debug("处理变量 {}, {}", var.getName(), var.getClassName());
					value = create.excute(type, tb);
					beanClazz.set(var.getName(), value);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
			
			content = beanClazz.getContent();
			
			String name = type.name();
			
			if(name.equalsIgnoreCase("JS_ADD_OR_EDIT") || name.equalsIgnoreCase("JS") || name.equalsIgnoreCase("JSP_ADD_OR_EDIT") || name.equalsIgnoreCase("JSP")){
				fileName = type.getType() + type.getFileSuffix();
			}else {
				fileName = tb.getEntityName() + type.getType() + type.getFileSuffix();
			}
			
			filePath = type.getFilePath() + fileName;
			// 按照模块放置生成文件的路径 
			if (filePath.contains("@{entity_package_type_lower}@")) {
				String tableName = tb.getTbName();
				String ppath = ASMConfig.TABLE_PACKAGE_MAP.get(tableName.trim());
				
				filePath = filePath.replace(parentPath + "/", "");
				filePath = filePath.replace("@{entity_package_type_lower}@",
						ppath);
			}
			
			if(filePath.contains("@{entity_name_lower_all}@")){
				filePath = filePath.replace("@{entity_name_lower_all}@", Utils.firstWordToLow(tb.getEntityName()));
			}
			
			LOG.info("生成文件路径：" + filePath);

			try {
				File file = new File(filePath);
				FileUtils.deleteQuietly(file);
				FileUtils.write(file, content, Charset.forName("UTF-8"));
			} catch (IOException e) {
				e.printStackTrace();
			}

		}

	}

	/**
	 * 从数据库中的表信息创建javabean,dao,service,controller
	 *
	 * @param
	 *
	 * @param packageName
	 *            导出类所在的包名,如：com.catt.pub.db.bean
	 * @param expPath
	 *            导出类的路径,如：./src/main/java/com/catt/pub/db/bean
	 * @param tableArray
	 *            选择要导出的表，为空则导出所有表
	 * @param tablePreArray
	 *            希望移除的（多个）表前缀
	 * @param x
	 *            变量，用于获取模板生成内容的判断，不同的值获取不同模板
	 * @throws Exception 
	 */
	public static void getBeanFromPDM(String pdmPath, String[] tableArray,
			String[] tablePreArray) throws Exception {
		List<TBStructure> tbs = getTBStructuresFromPDM(pdmPath, tableArray,
				tablePreArray);

		createSprintMVCs(tbs, tableArray, tablePreArray);

	}

	private static void createSprintMVCs(List<TBStructure> tbs,
			String[] tableArray, String[] tablePreArray) {
		if (tbs.size() == 0) {
			LOG.warn("找不到表！{}", Arrays.toString(tableArray));
		}
		for (TBStructure tb : tbs) {
			createSprintMVC(tb, tablePreArray);
		}
		LOG.info("success");
	}

	@SuppressWarnings({ "unchecked" })
	public static List<TBStructure> getTBStructuresFromPDM(String pdmPath,
			String[] tableArray, String[] tablePreArray) throws Exception {
		List<String> tableList = ((tableArray == null 
				|| tableArray.length == 0) ? null
				: Utils.strArray2List(tableArray));
		List<String> tablePreList = ((tablePreArray == null 
				|| tablePreArray.length == 0) ? 
				Utils.removeTablePres
				: Utils.strArray2List(tablePreArray));

		List<TBStructure> tbs = new ArrayList<TBStructure>();
		Map<String, String> xmlnsMap = new HashMap<String, String>();
		xmlnsMap.put("o", "object");
		SAXReader saxReader = new SAXReader();
		Reader reader = null;
		Document document = null;

		try {
			reader = new InputStreamReader(new FileInputStream(pdmPath),
					"UTF-8");
			document = saxReader.read(reader);
		} catch (Exception e) {
			throw new Exception("读取pdm文件失败", e);
		}
		XPath x = document.createXPath("//o:Table"); // 用XPath解析
		x.setNamespaceURIs(xmlnsMap);
		List<Element> elms = x.selectNodes(document);
		
		// 析取所有表对象
		for (Iterator<Element> elmIts = elms.iterator(); elmIts.hasNext();) {
			Element tableElm = elmIts.next();
			String tableName = tableElm.elementText("Code");
			if (tableList != null && !tableList.contains(tableName)) {
				continue;
			}
			TBStructure tb = new TBStructure();
			tb.setTbName(tableName);
			String entityName = Utils.getHumpTableName(tableName, tablePreList);
			tb.setEntityName(entityName);
			
			LOG.debug("*****************\r\n读取表 {} 结构", tableName);
			
			List<FieldSturcture> fields = getFieldsFromPDM(tableElm);
			tb.setFields(fields);
			tbs.add(tb);
		}
		return tbs;
	}

	/**
	 * 获取字段属性
	 *
	 * @param tableElm
	 * @return
	 */
	@SuppressWarnings("unchecked")
	private static List<FieldSturcture> getFieldsFromPDM(Element tableElm) {
		Iterator<Element> innerChild = tableElm.elementIterator();
		List<FieldSturcture> fields = new ArrayList<FieldSturcture>();	
		while (innerChild.hasNext()) {
			Element elements = innerChild.next();
			if (!elements.getName().equals("Columns")) {
				continue;
			}
			Iterator<Element> columnChild = elements.elementIterator();
			
			while (columnChild.hasNext()) {
				Element columns = columnChild.next();
				String colName = columns.elementText("Code");
				if ("id".equalsIgnoreCase(colName)
						|| "create_time".equalsIgnoreCase(colName)
						|| "modify_time".equalsIgnoreCase(colName)) {
					continue;
				}
				String fieldCnName = columns.elementText("Name");
				String fieldType = columns.elementText("DataType");
				
				LOG.debug("读取字段 {} {} 结构", colName, fieldCnName);
				
				String comment = columns.elementText("Comment");
				
				String length = "";
				if (fieldType.contains("(")) {
					length = RegexUtils.findBracketOnly("\\((\\d+)\\)", fieldType);
				}
				
				FieldSturcture field = new FieldSturcture();
				String propertyName = Utils.getHumpColumnName(colName, false);
				String methodName = Utils.getHumpColumnName(colName, true);
				String javaType = Utils.dataBaseToJavaType(fieldType);
				
				field.setColType(fieldType);
				field.setName(colName);
				field.setType(javaType);
				field.setPropertyName(propertyName);
				field.setMethodName(methodName);
				field.setComment(comment);
				field.setLength(length);
				
				LOG.debug("{}", field);
				
				fields.add(field);
			}
		}
		return fields;
	}

	/**
	 * 从数据库获取表结构
	 *
	 * @param conn
	 * @param tableArray
	 * @return
	 * @throws SQLException
	 */
	public static List<TBStructure> getTBStructuresFromDB(Connection conn,
			String[] tableArray, String[] tablePreArray) throws SQLException {
		List<String> tableList = ((tableArray == null 
				|| tableArray.length == 0) ? null
				: Utils.strArray2List(tableArray));

		List<String> tablePreList = ((tablePreArray == null 
				|| tablePreArray.length == 0) ? Utils.removeTablePres
				: Utils.strArray2List(tablePreArray));

		DatabaseMetaData dmd = conn.getMetaData();
		ResultSet tableInfos = Utils.checkDBType(dmd);

		List<TBStructure> tbs = new ArrayList<TBStructure>();

		// 迭代所有表，选择需要生成bean的表进行处理
		while (tableInfos.next()) {
			String tableName = tableInfos.getObject("TABLE_NAME").toString();
			if (tableName.toLowerCase().indexOf("bin$") != -1) {
				continue;
			}
			if (tableList != null && !tableList.contains(tableName)) {
				continue;
			}
			TBStructure tb = new TBStructure();
			tb.setTbName(tableName);
			LOG.debug("*****************\r\n读取表 {} 结构", tableName);
			String entityName = Utils.getHumpTableName(tableName, tablePreList);
			tb.setEntityName(entityName);

			List<FieldSturcture> fields = new ArrayList<FieldSturcture>();

			ResultSet colInfos = Utils.checkColumnsType(dmd, tableName);
			while (colInfos.next()) {
				String colName = colInfos.getString(4);
				if ("I_ID".equalsIgnoreCase(colName)
						|| "D_CREATE_DATE".equalsIgnoreCase(colName)
						|| "D_MODIFY_DATE".equalsIgnoreCase(colName)) {
					continue;
				}
				String length = "";
				String colType = colInfos.getString("TYPE_NAME");
				
				LOG.debug("读取字段 {}结构", colName);
				
			    length = colInfos.getString("COLUMN_SIZE");  //获取数据长度
			    length = Utils.charLength(colType, length);
			    
			    System.out.println(colName + ":" + length);
//			    String dataPrecision = colInfos.getString("DATA_PRECISION");  //获取数据长度
//			    String dataScale = colInfos.getString("DATA_SCALE");  //获取数据精度
//			    System.out.println(dataLength + "\t" + dataPrecision + "\t" + dataScale);
			    
				String javaType = Utils.dataBaseToJavaType(colType);
				
				String propertyName = Utils.getHumpColumnName(colName, false);
				String methodName = Utils.getHumpColumnName(colName, true);
				FieldSturcture field = new FieldSturcture();
				field.setName(colName);
				field.setType(javaType);
				field.setColType(colType);
				field.setPropertyName(propertyName);
				field.setMethodName(methodName);
				field.setLength(length);
				
				LOG.debug("{}", field);
				
				fields.add(field);
			}
			tb.setFields(fields);
			tbs.add(tb);
		}
		return tbs;
	}

	public static void main(String[] args) throws Exception {
		// PDM路径
		String pdmPath = "D:\\eclipseWork\\pdm-auto-spring-mvc\\j2ee-platform.pdm";
//		String pdmPath = "D:\\Scott\\myeclipseworkspace\\j2ee-platform.pdm";
		// 表名数组
		String[] tableArray = null;
		/*tableArray = new String[]{
				"demo_a",
				"demo_b",
				"demo_c",
				"demo_company",
				"demo_resource",
				"demo_role",
				"demo_role_rel_resource",
				"demo_user",
				"demo_user_rel_role",
				"pub_enum",
				"pub_menu",
				"pub_seq",
				"sec_account",
				"sec_account_group",
				"sec_account_rel_domain",
				"sec_account_rel_right",
				"sec_account_rel_role",
				"sec_data_domain",
				"sec_log_action",
				"sec_right",
				"sec_role",
				"sec_role_rel_domain",
				"sec_role_rel_right"
		};*/
		tableArray = new String[]{
				"sec_acount_test_tmp",
				"pub_account_root"
		};
		// 表名前缀数组
		String[] tablePreArray = new String[]{"pub_","sec_"};
		// 
		AutoSpringMVC.getBeanFromPDM(pdmPath, tableArray, tablePreArray);
	}
}
